Skip to content

refactor(workflows): compress pending_review_gate to emit + script-poll pattern#547

Merged
PolyphonyRequiem merged 2 commits into
mainfrom
refactor/pr-gate-compression-v2
May 29, 2026
Merged

refactor(workflows): compress pending_review_gate to emit + script-poll pattern#547
PolyphonyRequiem merged 2 commits into
mainfrom
refactor/pr-gate-compression-v2

Conversation

@PolyphonyRequiem

Copy link
Copy Markdown
Owner

Summary

Compresses the two pending_review_gate nodes in github-pr.yaml and ado-pr.yaml from blocking human_gate nodes into an emit + script-poll pattern, following the gate-compression-pattern ADR (docs/decisions/gate-compression-pattern.md).

Gates compressed: 2
Both pending_review_gate nodes were category-1 (deterministic-outcome): the workflow was pausing to wait for a human to take an action on the PR (merge, approve, comment, close) that Poll-PrStateDelta.ps1 can detect directly. They stay compressed.

Gates staying as human gates: 17
poll_error_gate, revise_cap_gate, remediation_cap_gate, stuck_review_gate, pr_pre_merge_gate, merge_failed_gate and others — all genuine judgment calls, config errors, or infrastructure failures. They are unchanged.


Per-file changes

github-pr.yaml (v2.4.8 → v2.5.0)

  • Added poll_timeout_seconds (default 86400 s / 24 h) and poll_interval_seconds (default 30 s) as optional workflow inputs
  • Added notifications: block (polyphony.github_pr namespace) with types pr_review_required and pr_ci_attention_required
  • Replaced three nodes (pending_poll_counter + pending_review_gate_policy_router + pending_review_gate) with:
    • notify_pr_pending — emits pr_review_required domain signal once when gate opens
    • poll_pr_state_delta — calls Poll-PrStateDelta.ps1; blocks until reaction or timeout
    • notify_pr_ci_attention — emits pr_ci_attention_required on CI FAILURE (Q3 Option C)
  • Added notify_pr_review_resolved before already_merged_emitter (emits disposition:resolved to close the pending CTA)
  • Updated stuck_review_gate prompt (removed pending_poll_counter template refs; timeout-based description)
  • Rewired stuck_review_resetpoll_pr_state_delta
  • Rewired pr_pre_merge_gate.deferpoll_pr_state_delta

ado-pr.yaml (v2.4.8 → v2.5.0)

Same pattern as GitHub with ADO-specific details:

  • poll_pr_state_delta uses -Platform ado; coords parsed from PrUrl by the script
  • notify_pr_pending payload includes organization and project
  • CI attention threshold uses 'failed' (ADO build policy status string)
  • ADO vote mapping documented in node comments (+5/+10 → approved, -10/-5 → changes_requested)

Script: Poll-PrStateDelta.ps1

Added Liszt's implementation (was untracked). One small contract gap patched:

WatermarkPath was mandatory in Liszt's implementation but optional in Wagner's plan §3.2. Made optional: when omitted, the path is auto-derived from PR coordinates (platform-owner-repo-number). This allows the YAML to omit -WatermarkPath entirely.

Reaction-kind contract note

Liszt's actual reaction_kind enum differs slightly from Wagner's plan. The YAML routing uses Liszt's actual values:

Liszt's value Meaning
initial_observation First call, watermark just established — YAML routes back to re-poll
pr_merged / pr_closed Terminal PR states
new_review_approved / new_review_changes_requested / new_review_commented Review reactions
new_commit / new_comment Commit/comment activity
ci_status_changed CI status flip (with delta_details.new for green/red routing)
timeout Budget elapsed — escalates to stuck_review_gate_policy_router

on_error: → poll_error_gate is deferred to AB#3257 (conductor RFC Phase 2 — on_error: in routes not yet shipped in v0.1.18).

Pester tests for the script (Poll-PrStateDelta.Tests.ps1) are included and were AST-clean per Liszt. Full test execution is follow-up work.


ADRs added

  • docs/decisions/gate-compression-pattern.md — the pattern this PR implements (Bach)
  • docs/decisions/domain-signal-envelope.md — dependency (Bach)
  • docs/decisions/polyphony-verb-error-boundary.md — dependency (Bach)
  • docs/north-star.md — high-level vision doc (Bach)

Open follow-ups

  • AB#3257: Add on_error: to: poll_error_gate to poll_pr_state_delta once conductor RFC Phase 2 ships
  • Post upstream PR Closed-loop step 2: wire PlanObserver into state next-ready #213 cherry-pick (27006af): bulk-rename type: notificationtype: emit and notification:emit: in both files
  • Run Poll-PrStateDelta.Tests.ps1 in CI
  • Thread poll_timeout_seconds / poll_interval_seconds through feature-pr.yaml and implement-merge-group.yaml callers

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Daniel Green and others added 2 commits May 29, 2026 11:21
…ll pattern

Implements the gate-compression-pattern ADR (docs/decisions/gate-compression-pattern.md)
for the two pending_review_gate nodes across github-pr.yaml and ado-pr.yaml.

## Gates compressed (2)
- github-pr.yaml: pending_review_gate (line 1025 on main)
- ado-pr.yaml:    pending_review_gate (line 1146 on main)

Both gates were category-1 (deterministic-outcome): the workflow was pausing to
wait for a human to take an action on the PR (merge, approve, comment, or close)
that Poll-PrStateDelta.ps1 can detect directly.

## Vestigial cleanup (Q5 Option A)
- Removed pending_poll_counter per-file (counter logic superseded by TimeoutSeconds)
- Removed pending_review_gate_policy_router per-file (skip/wait/auto modes superseded)
- Rewired stuck_review_reset -> poll_pr_state_delta (router gone)
- Rewired pr_pre_merge_gate.defer -> poll_pr_state_delta (gate gone)
- Updated stuck_review_gate prompts to remove pending_poll_counter template refs

## Per-file changes

### github-pr.yaml
- Added poll_timeout_seconds / poll_interval_seconds optional inputs (default 86400 / 30)
- Added notifications: block (polyphony.github_pr namespace)
- Added types: pr_review_required, pr_ci_attention_required
- pr_feedback_analyzer catch-all: pending_poll_counter -> notify_pr_pending
- New nodes: notify_pr_pending, poll_pr_state_delta, notify_pr_ci_attention
- New node: notify_pr_review_resolved (inserted before already_merged_emitter)
- Version bump: 2.4.8 -> 2.5.0

### ado-pr.yaml
- Same structure as github-pr.yaml with ADO-specific args and payload fields
- poll_pr_state_delta uses -Platform ado (coords parsed from PrUrl by script)
- ci_status_changed FAILURE threshold uses 'failed' (ADO build policy status)
- Version bump: 2.4.8 -> 2.5.0

## Script: Poll-PrStateDelta.ps1
- Added Liszt's implementation (scripts/Poll-PrStateDelta.ps1 + Tests)
- Contract gap patched: WatermarkPath is now optional; auto-derived from
  PR coords (platform+owner+repo+number) when not supplied by caller.
  This aligns with Wagner's plan §3.2 and allows the YAML to omit -WatermarkPath.
- Tests (Poll-PrStateDelta.Tests.ps1) included as-is; AST-clean per Liszt.
  Full test execution is follow-up work.

## Script contract gaps noted (for follow-up)
- reaction_kind enum in script differs from Wagner's plan:
  pr_merged/pr_closed (not merged/closed), ci_status_changed (not ci_changed),
  new_review_approved/changes_requested/commented (not new_review).
  YAML routing uses Liszt's actual values throughout.
- initial_observation is a new kind (first call, no watermark); YAML routes
  it back to poll_pr_state_delta to re-poll immediately.
- on_error: -> poll_error_gate deferred to AB#3257 (conductor RFC Phase 2).

## ADRs added
- docs/decisions/gate-compression-pattern.md (Bach)
- docs/decisions/domain-signal-envelope.md (Bach, dependency of above)
- docs/decisions/polyphony-verb-error-boundary.md (Bach, dependency of above)
- docs/north-star.md (Bach)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…stub

Five pre-existing bugs surfaced when running Pester locally:

1. Script: Invoke-AssertCli referenced `.Exe`, a property that
   doesn't exist on the returned object. Under Set-StrictMode -Version
   Latest this throws PropertyNotFoundException before the auth / 404 /
   rate-limit catch chain runs, collapsing 401/404/429 all to exit
   code 5. Fixed by removing the redundant `-not `.Exe -and`
   guard (ExitCode -eq -1 plus the 'not found on PATH' regex already
   identify this case unambiguously).

2. Test: Build-GhStub's PR-core pattern 'api repos/*/pulls/* --jq *'
   also matched '/pulls/{n}/reviews' because '*' greedily eats the URL
   tail. Tightened to '--jq {*' so the brace discriminates against
   the reviews/comments/CI paths (whose jq filters start with '[').

3. Script: Find-GitHubDeltas / Find-AdoDeltas return a typed
   List[hashtable] via the PowerShell pipeline. An empty list yields
   ``; a single-item list yields that one hashtable — neither has
   a reliable .Count. Wrapped the call-site assignment in `@()` so
   `` is always a plain array.

4. Script: Select-HighestPrecedenceDelta declared its parameter as
   [System.Collections.Generic.List[hashtable]]. Under StrictMode,
   passing a plain hashtable (single-delta case) triggered a
   PropertyNotFoundException during argument binding. Removed the
   type constraint — the function body only iterates and accesses
   hashtable keys, so no type hint is needed.

5. Test: ConvertFrom-Json auto-promotes ISO-8601 date strings to
   System.DateTime objects; the Should -Match regex then ran against
   DateTime.ToString() which does not produce 'Zulu' notation.
   Fixed by normalising to string with the expected format before
   asserting in 'Always emits observed_at_utc in ISO 8601 format'.
   Also bumped TimeoutSeconds to 30 for the 'pr_merged + new_commit'
   precedence test whose CI-stub subprocess calls made one full poll
   exceed the prior 10-second ceiling under load.

Pester now 30/30 green (1 slow-test skip intentional) on the
refactor/pr-gate-compression-v2 branch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@PolyphonyRequiem

Copy link
Copy Markdown
Owner Author

📊 Pester verification — 5 pre-existing bugs caught + patched

Ran scripts/Poll-PrStateDelta.Tests.ps1 locally on this branch and found 5 bugs that pre-date this PR:

  1. ScriptInvoke-AssertCli accessed nonexistent \$Result.Exe under StrictMode → PropertyNotFoundException collapsed 401/404/429 to exit code 5. Fixed by removing the redundant guard (ExitCode -eq -1 + 'not found on PATH' regex already suffices).

  2. TestBuild-GhStub PR-core glob 'api repos/*/pulls/* --jq *' also matched /pulls/{n}/reviews because * is greedy. Tightened to --jq {* (jq filter for the PR-core call starts with {).

  3. ScriptFind-GitHubDeltas returns a typed List[hashtable] through the pipeline: empty list → \, single item → bare hashtable. \.Count was unreliable in both cases. Wrapped call-site in @() to always produce a plain array.

  4. ScriptSelect-HighestPrecedenceDelta declared [System.Collections.Generic.List[hashtable]]\. StrictMode threw PropertyNotFoundException when a plain hashtable (single-delta) was passed. Removed the type constraint.

  5. TestConvertFrom-Json auto-promotes ISO-8601 strings to DateTime objects; Should -Match was receiving a DateTime's ToString() output. Fixed by normalising to string before asserting. Also bumped one test's TimeoutSeconds from 10→30 so CI-stub subprocess latency doesn't cause a spurious timeout.

Fixed in commit b37d85f. Pester is now 30/30 green (1 intentional skip for the 60s+ hang test).

— Liszt

@PolyphonyRequiem PolyphonyRequiem merged commit eab39cb into main May 29, 2026
1 check failed
@PolyphonyRequiem PolyphonyRequiem deleted the refactor/pr-gate-compression-v2 branch May 29, 2026 20:43
PolyphonyRequiem pushed a commit that referenced this pull request May 31, 2026
- Merged 2 inbox files into decisions.md (user directive on Wagner's PR-gate defaults, Wagner's post-ship notes).
- Updated liszt/history.md: append note on Wagner's WatermarkPath optional patch (PR #547, poll-pr-state-delta.ps1).
- Orchestration and session logs written to gitignored directories (orchestration-log/, log/).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PolyphonyRequiem pushed a commit that referenced this pull request May 31, 2026
… bugs for Daniel)

- Merged .squad/decisions/inbox/ entries (Sibelius collision check + Liszt Pester verification)
- Updated Wagner & Liszt history files with cross-agent verification notes
- No archiving needed (all decisions <= 7 days old)
- Sibelius verdict: AB#3257 covers both on_error retrofit + abandoned_on_error Phase 2 option
- Liszt verdict: PR #547 Pester FAIL (22/31) — two bugs block merge until fixed

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PolyphonyRequiem pushed a commit that referenced this pull request May 31, 2026
…30 green)

- Merged inbox decision: Liszt-poll-pr-state-delta-fix-shipped.md
- Updated Wagner history.md with fix summary
- Archived Wagner history.md to history-archive.md (15KB threshold exceeded)
- No entries older than 7 days to archive from decisions.md
PolyphonyRequiem pushed a commit that referenced this pull request May 31, 2026
- Merged PR #546 (seed-manifest ADR) at 619ce7c
- Merged PR #547 (PR-gate compression + Liszt bug fixes) at eab39cb
- Inbox decision entry merged into decisions.md
- Cross-agent history notes appended to Bach, Wagner, Liszt

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant